#include "SentenceBuilder.h"

#include "ScriptCompiler/ScriptFileContext.h"

#include "ScriptCompiler/ASTBuilder/IfBuilder.h"
#include "ScriptCompiler/ASTBuilder/WhileBuilder.h"
#include "ScriptCompiler/ASTBuilder/DoWhileBuilder.h"
#include "ScriptCompiler/ASTBuilder/ForBuilder.h"
#include "ScriptCompiler/ASTBuilder/SwitchBuilder.h"
#include "ScriptCompiler/ASTBuilder/ReturnBuilder.h"
#include "ScriptCompiler/ASTBuilder/BreakBuilder.h"
#include "ScriptCompiler/ASTBuilder/ContinueBuilder.h"
#include "ScriptCompiler/ASTBuilder/VariableDeclarationBuilder.h"
#include "ScriptCompiler/ASTBuilder/ExpressionBuilder.h"

#include "ScriptCompiler/AST/IfNode.h"
#include "ScriptCompiler/AST/WhileNode.h"
#include "ScriptCompiler/AST/DoWhileNode.h"
#include "ScriptCompiler/AST/ForNode.h"
#include "ScriptCompiler/AST/SwitchNode.h"
#include "ScriptCompiler/AST/ReturnNode.h"
#include "ScriptCompiler/AST/BreakNode.h"
#include "ScriptCompiler/AST/ContinueNode.h"

CSentenceBuilder::CSentenceBuilder(
    std::shared_ptr<CScriptFileContext> InContext,
    TOptional<int64_t> InEndTokenIndex
    )
{
    Context = InContext;
    EndTokenIndex = InEndTokenIndex;
}

bool CSentenceBuilder::ReadCurentToken()
{
    auto Token = Context->GetCurrentToken();

    switch(Token->Type)
    {
        case EScriptTokenType::If:
        {
            auto IfBuilder = std::make_shared<CIfBuilder>(Context);
            if (!IfBuilder->ReadCurentToken())
            {
                return false;
            }
            Result=IfBuilder->Result;
            return true;
        }

        case EScriptTokenType::Switch:
        {
            auto SwitchBuilder = std::make_shared<CSwitchBuilder>(Context);
            if (!SwitchBuilder->ReadCurentToken())
            {
                return false;
            }
            Result=SwitchBuilder->Result;
            return true;
        }

        case EScriptTokenType::While:
        {
            auto WhileBuilder = std::make_shared<CWhileBuilder>(Context);
            if (!WhileBuilder->ReadCurentToken())
            {
                return false;
            }
            Result=WhileBuilder->Result;
            return true;
        }

        case EScriptTokenType::Do:
        {
            auto DowhileBuilder = std::make_shared<CDoWhileBuilder>(Context);
            if (!DowhileBuilder->ReadCurentToken())
            {
                return false;
            }
            Result=DowhileBuilder->Result;
            return true;
        }

        case EScriptTokenType::For:
        {
            auto ForBuilder = std::make_shared<CForBuilder>(Context);
            if (!ForBuilder->ReadCurentToken())
            {
                return false;
            }
            Result=ForBuilder->Result;
            return true;
        }

        case EScriptTokenType::Return:
        {
            auto ReturnBuilder = std::make_shared<CReturnBuilder>(Context);
            if (!ReturnBuilder->ReadCurentToken())
            {
                return false;
            }
            Result=ReturnBuilder->Result;
            return true;
        }

        case EScriptTokenType::Break:
        {
            auto BreakBuilder = std::make_shared<CBreakBuilder>(Context);
            if (!BreakBuilder->ReadCurentToken())
            {
                return false;
            }
            Result=BreakBuilder->Result;
            return true;
        }

        case  EScriptTokenType::Continue:
        {
            auto ContinueBuilder = std::make_shared<CContinueBuilder>(Context);
            if (!ContinueBuilder->ReadCurentToken())
            {
                return false;
            }
            Result=ContinueBuilder->Result;
            return true;
        }

        default:
        {
            //test if this is a variable declaration
            if(CVariableDecalarationBuilder::DetectVariableDeclaration(Context))
            {
                auto VariableDeclarationBuilder = std::make_shared<CVariableDecalarationBuilder>(Context);
                if (!VariableDeclarationBuilder->ReadCurentToken())
                {
                    return false;
                }
                Result=VariableDeclarationBuilder->Result;
                return true;
            }

            int64_t _EndTokenIndex=-1;
            if(EndTokenIndex.HasValue())
            {
                _EndTokenIndex=EndTokenIndex.GetValue();
                assert(_EndTokenIndex>=Context->CurrentTokenIndex);
            }
            else
            {
                auto SemiColonIndex = FindNextToken(EScriptTokenType::Semicolon, Context);
                if (!SemiColonIndex.HasValue())
                {
                    Context->RaiseError(10091,Context->GetCurrentToken(), U"Expect a ';' at end of sentence");
                    return false;
                }
                _EndTokenIndex=SemiColonIndex.GetValue();
            }
        
            auto ExpressionBuilder = std::make_shared<CExpressionBuilder>(Context, _EndTokenIndex);
            if (!ExpressionBuilder->ReadCurentToken())
            {
                return false;
            }
            if(ExpressionBuilder->Result->Type!=EASTNodeType::Empty)
            {
                std::shared_ptr<CExpressionSentence> SentenceNode = std::make_shared<CExpressionSentence>(Context);
                SentenceNode->Expression=ExpressionBuilder->Result;
                Result=SentenceNode;
            }

            //skip ';'
            assert(_EndTokenIndex == Context->CurrentTokenIndex);
            Context->CurrentTokenIndex++;
            return true;
        }
    }

    assert(false && "unreachable" );
    return false;
    
}
